/*
 *
 *  Copyright (C) 2010 Tal Eisenberg
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */


package fork;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import tal.drivechain.chain.AbortChainException;
import tal.drivechain.chain.Chain;
import tal.drivechain.chain.ChainNode;
import tal.drivechain.plugin.Channel;
import tal.drivechain.plugin.Module;
import tal.drivechain.system.Globals;
import tal.drivechain.variable.types.DataType;
import tal.drivechain.variable.types.HashmapType;
import tal.drivechain.variable.SetProperty;
import tal.drivechain.variable.SetProperty.Flag;
import tal.drivechain.variable.Variable;

/**
 *
 * @author Main
 */
public class Logger extends Module {
    InnerLogger innerLogger;
    List<Chain> logChains;

    Variable out = new Variable("log", new HashmapType(DataType.STRING), new HashMap(), true);
    
    @Override
    public void setupProperties() throws IllegalArgumentException, AbortChainException {
        innerLogger = new InnerLogger();
        innerLogger.init(false, null);
        super.setupProperties();
    }

    public void setUseTiming(Object value, SetProperty.Flag flag) {
        innerLogger.setTiming((Boolean)value);
        String msg;
        if (innerLogger.printsTiming()) msg = "Logger will now display timing data";
        else msg = "Logger will ignore timing data.";
        innerLogger.nodeMessage(msg, MSG.INFO, this);

    } public Object getUseTiming() { return innerLogger.printsTiming(); }

    public void setLogChain(Object value, SetProperty.Flag flag) {
        List<String> newTrigMsg = (List<String>)value;

        if (logChains==null) logChains = new ArrayList<Chain>();
        else if (flag != Flag.ADD) {
            for (Chain c : logChains)
                c.removeLogger(innerLogger);
            logChains.clear();
        }
        for (String chainPath : newTrigMsg) {
            logChains.add(Globals.findChain(chainPath, getParentChain()));
        }

        for (Chain c : logChains)
            c.addLogger(innerLogger);

        // since the old chain might have contained the inputs of the Logger mod, we need to reset them.
        for (ChainNode n : this.getInputs()) n.addLogger(innerLogger);

    } public Object getLogChain() {
        if (logChains==null) return null;
        
        List<String> chainPaths = new ArrayList<String>();
        for (Chain c : logChains) chainPaths.add(c.getPath());
        return chainPaths;
    }

    @Override
    public void start() {
        super.start();
        String from = "";
        for (ChainNode p : getInputs()) {
            from += (p.getName() + " (" + p.getClass().getSimpleName() + "), ");
            if (!p.getLoggers().contains(innerLogger))
                p.addLogger(innerLogger);
        }
        innerLogger.runLogger();
        log("Logger will process messages from: " + from.substring(0, from.length()-2), MSG.INFO);
    }

    @Override
    public void stop() {
        for (ChainNode p : getInputs()) {
            p.removeLogger(innerLogger);
        }
        log("Logger stopped.", MSG.ANOUNCE);
        innerLogger.stopLogger();
        super.stop();
    }

    @Override
    public void addInput(ChainNode input) {
        super.addInput(input);
        if (this.isRunning()) {
            input.addLogger(innerLogger);
            log("Will display logs from " + input.getName() + " (" + input.getClass().getSimpleName() + ")", MSG.ANOUNCE);
        }
    }

    @Override
    public boolean removeInput(ChainNode a) {
        if (super.removeInput(a)) { a.removeLogger(innerLogger); return true; }
        else return false;
    }

    @Override
    protected void msgReceived(Variable in, long timestamp, Channel channel) {
    }

    protected class InnerLogger extends tal.drivechain.plugin.Logger {

        @Override
        public void logReceived(LogLine l) {
            if (!isRunning()) return;

            HashMap hash = (HashMap)out.getInternalValue();
            hash.put("type", l.type.name().toLowerCase());
            if (printsTiming())
                hash.put("timing", l.time);
            else hash.remove("timing");
            hash.put("source", l.src);
            hash.put("message", l.message);
            sendToOutputs(out, -1); 
        }

        @Override
        public void println(String line) {
            if (!isRunning()) return;

            HashMap hash = (HashMap)out.getInternalValue();
            hash.put("source", "");
            hash.put("message", line);
            hash.put("type","line");
            new Thread() { public void run() { sendToOutputs(out, -1); } }.start();
        }
    }
    
}
